package com.dfe.backend.common.sax;

import cn.hutool.core.util.StrUtil;
import com.dfe.backend.common.bean.IedInfo;
import com.dfe.backend.common.sax.scd.ScdData;
import com.dfe.backend.common.sax.scd.ScdFilter;
import com.dfe.backend.common.sax.scd.communication.*;
import com.dfe.backend.common.sax.scd.ied.*;
import net.roseboy.classfinal.util.StrUtils;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

import java.util.*;

/**
 * 【描述】：SCD分析处理器
 *
 * @author LiChang
 * @since 2025-7-28 15:07
 */
public class ScdServiceHandler extends DefaultHandler {

    public ScdData configData;
    public List<Ied> iedList = new ArrayList<>();
    public List<String> iedNameList = new ArrayList<>();
    public List<IedInfo> iedInfoList = new ArrayList<>();
    public Map<String, String> iedNameMap = new HashMap<>();

    public Map<String, String> appIdMap = new HashMap<>();

    public Map<String, List<ExtRef>> iedExtRefMap = new HashMap<>();
    public Map<String, Map<String, String>> iedDataSetGseMap = new HashMap<>();
    public Map<String, Map<String, String>> iedDataSetSvMap = new HashMap<>();
    private Map<String, String> pathToDataSetMap = new HashMap<>();
    private Map<String, String> referencePathMap = new HashMap<>();
    private Communication communication;
    private List<SubNetwork> subNetworkList;
    private SubNetwork subNetwork;
    private List<ConnectedAP> connectedAPList;
    private ConnectedAP connectedAP;
    private List<GSE> gseList;
    private GSE gse;
    // 判定是否为GSE下的Address标记
    private boolean isGseAddressFlag = false;
    private List<SMV> smvList;
    private SMV smv;
    private boolean isSmvAddressFlag = false;
    private Address address;

    private List<P> pList;
    private P p;

    /**
     * 判断是否在P节点内部
     */
    private boolean isInTagP;
    private String nodeValueP;

    private Ied ied;
    private List<ExtRef> extRefList;
    private ExtRef extRef;
    private List<Inputs> inputsList;
    private Inputs inputs;
    private AccessPoint accessPoint;
    private List<AccessPoint> accessPointList;
    private LDevice ldevice;
    private List<LDevice> lDeviceList;
    private LN0 ln0;
    private LN ln;
    private List<LN> lnList;
    private DOI doi;
    private List<DOI> doiList;
    private DAI dai;
    private List<DAI> daiList;
    private Val val;
    private DataSet dataSet;
    private List<DataSet> dataSetList;

    private FCDA fcda;
    private List<FCDA> fcdaList;
    private Object currentNode;

    private List<String> gooseIedNames;
    private List<String> svIedNames;

    private String tempIedName = "";


    StringBuilder sb = new StringBuilder();

    /**
     * 判断是否在Val节点内部
     */
    private boolean isInTag;
    private String nodeValue;


    private List<ExtRef> allExtRefList;

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) {

        switch (qName) {
            case "SCL":
                configData = new ScdData();
                allExtRefList = new ArrayList<>();
                communication = new Communication();
                break;
            case "Communication":
                subNetworkList = new ArrayList<>();
                break;
            case "SubNetwork":
                subNetwork = new SubNetwork();
                subNetwork.setName(attributes.getValue("name"));
                subNetwork.setType(attributes.getValue("type"));
                connectedAPList = new ArrayList<>();
                break;
            case "ConnectedAP":
                connectedAP = new ConnectedAP();
                connectedAP.setApName(attributes.getValue("apName"));
                connectedAP.setDesc(attributes.getValue("desc"));
                connectedAP.setIedName(attributes.getValue("iedName"));
                gseList = new ArrayList<>();
                smvList = new ArrayList<>();
                break;
            case "GSE":
                gse = new GSE();
                gse.setLdInst(attributes.getValue("ldInst"));
                gse.setCbName(attributes.getValue("cbName"));
                isGseAddressFlag = true;
                break;
            case "SMV":
                smv = new SMV();
                smv.setLdInst(attributes.getValue("ldInst"));
                smv.setCbName(attributes.getValue("cbName"));
                isSmvAddressFlag = true;
                break;
            case "Address":
                if (isGseAddressFlag || isSmvAddressFlag) {
                    address = new Address();
                    pList = new ArrayList<>();
                }
                break;
            case "P":
                if (isGseAddressFlag || isSmvAddressFlag) {
                    p = new P();
                    p.setType(attributes.getValue("type"));
                    isInTagP = true;
                }
                break;
            case "IED":
                ied = new Ied();
                String iedName = attributes.getValue("name");
                String iedDesc = attributes.getValue("desc");
                tempIedName = attributes.getValue("name");
                ied.setDesc(iedDesc);
                ied.setName(iedName);
                accessPointList = new ArrayList<>();
                gooseIedNames = new ArrayList<>();
                svIedNames = new ArrayList<>();
                if (StrUtil.isNotEmpty(iedName)) {
                    iedNameList.add(iedName);
                    iedNameMap.put(iedName, iedDesc);
                    iedInfoList.add(new IedInfo(iedName, iedDesc));
                    iedExtRefMap.put(iedName, new ArrayList<>());
                }
                break;
            case "AccessPoint":
                accessPoint = new AccessPoint();
                accessPoint.setName(attributes.getValue("name"));
                lDeviceList = new ArrayList<>();
                break;
            case "LDevice":
                ldevice = new LDevice();
                ldevice.setDesc(attributes.getValue("desc"));
                ldevice.setInst(attributes.getValue("inst"));
                lnList = new ArrayList<>();
                ln = null;
                ln0 = null;
                break;
            case "LN0":
                ln0 = new LN0();
                ln0.setDesc(attributes.getValue("desc"));
                ln0.setInst(attributes.getValue("inst"));
                ln0.setLnClass(attributes.getValue("lnClass"));
                if (Objects.isNull(attributes.getValue("prefix"))) {
                    ln0.setPrefix("");
                } else {
                    ln0.setPrefix(attributes.getValue("prefix"));
                }
                currentNode = ln0;
                doiList = new ArrayList<>();
                inputs = new Inputs();
                dataSetList = new ArrayList<>();
                inputsList = new ArrayList<>();
                break;
            case "LN":
                ln = new LN();
                ln.setDesc(attributes.getValue("desc"));
                ln.setInst(attributes.getValue("inst"));
                ln.setLnClass(attributes.getValue("lnClass"));
                if (Objects.isNull(attributes.getValue("prefix"))) {
                    ln.setPrefix("");
                } else {
                    ln.setPrefix(attributes.getValue("prefix"));
                }
                currentNode = ln;
                doiList = new ArrayList<>();
                break;
            case "DataSet":
                dataSet = new DataSet();
                dataSet.setDesc(attributes.getValue("desc"));
                dataSet.setName(attributes.getValue("name"));
                fcdaList = new ArrayList<>();
                break;
            case "FCDA":
                fcda = new FCDA();
                if (Objects.isNull(attributes.getValue("daName"))) {
                    fcda.setDaName("");
                } else {
                    fcda.setDaName(attributes.getValue("daName"));
                }
                fcda.setDoName(attributes.getValue("doName"));
                fcda.setFc(attributes.getValue("fc"));
                fcda.setLdInst(attributes.getValue("ldInst"));
                fcda.setLnClass(attributes.getValue("lnClass"));
                if (Objects.isNull(attributes.getValue("lnInst"))) {
                    fcda.setLnInst("");
                } else {
                    fcda.setLnInst(attributes.getValue("lnInst"));
                }
                if (Objects.isNull(attributes.getValue("prefix"))) {
                    fcda.setPrefix("");
                } else {
                    fcda.setPrefix(attributes.getValue("prefix"));
                }
                fcdaList.add(fcda);
                // [iedName][ldInst].[prefix][lnClass][lnInst].doName.daName
                String key = tempIedName + fcda.getLdInst() + "." + fcda.getPrefix() + fcda.getLnClass() + fcda.getLnInst() + "." + fcda.getDoName();
                // 发送端Reference： [iedName][ldInst]/[prefix][lnClass][lnInst]$[FC]$doName$daName
                String reference = tempIedName + fcda.getLdInst() + "/" + fcda.getPrefix() + fcda.getLnClass() + fcda.getLnInst() + "$" + fcda.getFc() + "$" + fcda.getDoName();
                if (StrUtil.isNotEmpty(fcda.getDaName())) {
                    key = key + "." + fcda.getDaName();
                    reference = reference + "$" + fcda.getDaName();
                }
                pathToDataSetMap.put(key, dataSet.getName());
                referencePathMap.put(key, reference);
                break;
            case "Inputs":
                extRefList = new ArrayList<>();
                break;
            case "ExtRef":
                extRef = new ExtRef();
                extRef.setIntAddr(attributes.getValue("intAddr"));
                extRef.setIedName(attributes.getValue("iedName"));
                extRef.setLdInst(attributes.getValue("ldInst"));
                extRef.setPrefix(attributes.getValue("prefix"));
                extRef.setLnClass(attributes.getValue("lnClass"));
                extRef.setLnInst(attributes.getValue("lnInst"));
                extRef.setDoName(attributes.getValue("doName"));
                extRef.setDaName(attributes.getValue("daName"));
                // 接收端IED名称
                extRef.setReceiveIedName(tempIedName);
                //g1访问点
                if (accessPoint.getName().toLowerCase().contains("g") && !gooseIedNames.contains(attributes.getValue("iedName"))) {
                    gooseIedNames.add(attributes.getValue("iedName"));
                }
                //m1访问点
                if (accessPoint.getName().toLowerCase().contains("m") && !svIedNames.contains(attributes.getValue("iedName"))) {
                    svIedNames.add(attributes.getValue("iedName"));
                }
                extRefList.add(extRef);
                break;
            case "GSEControl":
                // iedName +"."+ accessPoint.getName()
                String gseKey = tempIedName + "." + accessPoint.getName();
                Map<String, String> gseDataSetMap = iedDataSetGseMap.get(gseKey);
                if (gseDataSetMap == null) {
                    gseDataSetMap = new HashMap<>();
                }
                gseDataSetMap.put(attributes.getValue("datSet"), attributes.getValue("name"));
                iedDataSetGseMap.put(gseKey, gseDataSetMap);
                break;
            case "SampledValueControl":
                String svKey = tempIedName + "." + accessPoint.getName();
                Map<String, String> svDataSetMap = iedDataSetSvMap.get(svKey);
                if (svDataSetMap == null) {
                    svDataSetMap = new HashMap<>();
                }
                svDataSetMap.put(attributes.getValue("datSet"), attributes.getValue("name"));
                iedDataSetSvMap.put(svKey, svDataSetMap);
                break;
            case "DOI":
                doi = new DOI();
                doi.setDesc(attributes.getValue("desc"));
                doi.setName(attributes.getValue("name"));
                daiList = new ArrayList<>();
                break;
            case "DAI":
                dai = new DAI();
                dai.setName(attributes.getValue("name"));
                break;
            case "Val":
                val = new Val();
                isInTag = true;
                break;
            default:
                break;
        }

    }

    @Override
    public void characters(char[] ch, int start, int length) {
        if (isInTag) {
            sb.append(ch, start, length);
            nodeValue = sb.toString();
        }
        if (isInTagP) {
            sb.append(ch, start, length);
            nodeValueP = sb.toString();
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) {
        switch (qName) {
            case "SCL":
                configData.setIedList(iedList);
                configData.setAllExtRefList(allExtRefList);
                configData.setCommunication(communication);
                configData.setAppIdMap(appIdMap);
                // 基础信息
                configData.setIedNameList(iedNameList);
                configData.setIedInfoList(iedInfoList);
                configData.setIedNameMap(iedNameMap);
                // IED与连接关系MAP
                configData.setIedExtRefMap(iedExtRefMap);
                // IED和数据集，cbName定位关系
                configData.setIedDataSetGseMap(iedDataSetGseMap);
                configData.setIedDataSetSvMap(iedDataSetSvMap);
                configData.setPathToDataSetMap(pathToDataSetMap);
                configData.setReferencePathMap(referencePathMap);
                break;
            case "Communication":
                communication.setSubNetworkList(subNetworkList);
                break;
            case "SubNetwork":
                subNetwork.setConnectedAPList(connectedAPList);
                subNetworkList.add(subNetwork);
                break;
            case "ConnectedAP":
                connectedAP.setGseList(gseList);
                connectedAP.setSmvList(smvList);
                connectedAPList.add(connectedAP);
                break;
            case "GSE":
                isGseAddressFlag = false;
                gse.setAddress(address);
                gseList.add(gse);
                break;
            case "SMV":
                isSmvAddressFlag = false;
                smv.setAddress(address);
                smvList.add(smv);
                break;
            case "Address":
                if (isGseAddressFlag || isSmvAddressFlag) {
                    address.setPList(pList);
                }
                break;
            case "P":
                if (isGseAddressFlag || isSmvAddressFlag) {
                    isInTagP = false;
                    String type = p.getType();
                    if (StrUtils.isNotEmpty(type)) {
                        p.setValue(nodeValueP);
                        pList.add(p);
                        if ("APPID".equals(type)) {
                            String tailName;
                            if (isGseAddressFlag) {
                                tailName = gse.getCbName() + "." + gse.getLdInst();
                            } else {
                                tailName = smv.getCbName() + "." + smv.getLdInst();
                            }
                            String key = connectedAP.getIedName() + "." + connectedAP.getApName() + "." + tailName;
                            appIdMap.put(key, nodeValueP);
                        }
                    }
                }
                break;
            case "IED":
                ied.setAccessPointList(accessPointList);
                iedList.add(ied);
                ied.setGooseIedNames(gooseIedNames);
                ied.setSvIedNames(svIedNames);
                tempIedName = "";
                break;
            case "AccessPoint":
                accessPoint.setLDeviceList(lDeviceList);
                accessPointList.add(accessPoint);
                break;
            case "LDevice":
                ldevice.setLn0(ln0);
                ldevice.setLnList(lnList);
                lDeviceList.add(ldevice);
                break;
            case "LN0":
                //在doiList中过滤出dai的name为dU的内容
                ScdFilter.filter(doiList);
                ln0.setDoiList(doiList);
                ln0.setDataSetList(dataSetList);
                ln0.setInputsList(inputsList);
                break;
            case "LN":
                //在doiList中过滤出dai的name为dU的内容
                ScdFilter.filter(doiList);
                ln.setDoiList(doiList);
                lnList.add(ln);
                break;
            case "DataSet":
                dataSet.setFcdaList(fcdaList);
                dataSetList.add(dataSet);
                break;
            case "FCDA":
                break;
            case "Inputs":
                inputs.setExtRefList(extRefList);
                inputsList.add(inputs);
                // 添加所有extRef
                allExtRefList.addAll(extRefList);
                break;
            case "ExtRef":
                break;
            case "GSEControl":
                break;
            case "DOI":
                doi.setDaiList(daiList);
                doiList.add(doi);
                // 查看此Ied下的dataset 是否有路径和此doi联系，有的话则更新dataset路径的描述
                if (!Objects.isNull(dataSetList)) {
                    for (DataSet dataSet : dataSetList) {
                        for (FCDA fcda : dataSet.getFcdaList()) {
                            String ldInst = ldevice.getInst();
                            String lnClass, lnInst, prefix;
                            if (!Objects.isNull(ln)) {
                                lnClass = ln.getLnClass();
                                lnInst = ln.getInst();
                                prefix = ln.getPrefix();
                            } else {
                                lnClass = ln0.getLnClass();
                                lnInst = ln0.getInst();
                                prefix = ln0.getPrefix();
                            }
                            if (Objects.isNull(lnInst)) {
                                lnInst = "";
                            }
                            String doName = doi.getName();

                            if (fcda.getDoName().equals(doName) && fcda.getLdInst().equals(ldInst)
                                    && fcda.getLnClass().equals(lnClass) && fcda.getLnInst().equals(lnInst)
                                    && fcda.getPrefix().equals(prefix)) {
                                fcda.setDesc(doi.getDesc());
                            }
                        }
                    }
                }
                break;
            case "DAI":
                dai.setVal(val);
                daiList.add(dai);

                // 查看此Ied下的inputs 是否有内部路径和此doi联系，有的话则更新inputs下内部路径的内部描述
                if (!Objects.isNull(extRefList)) {
                    for (ExtRef ref : extRefList) {
                        Boolean flag = ref.getIsAbsoluteMatchReceiveDescFlag();
                        if (flag != null && flag) {
                            continue;
                        }
                        String ldInst = ldevice.getInst();
                        // 存在LN
                        String lnClass, lnInst, lnPrefix;
                        if (!Objects.isNull(ln)) {
                            lnClass = ln.getLnClass();
                            lnInst = ln.getInst();
                            lnPrefix = ln.getPrefix();
                        } else {
                            lnClass = ln0.getLnClass();
                            lnInst = ln0.getInst();
                            lnPrefix = ln0.getPrefix();
                        }
                        if (StrUtil.isEmpty(lnPrefix)) {
                            lnPrefix = "";
                        }
                        String tempDa = ldInst + "/" + lnPrefix + lnClass + lnInst + "." + doi.getName() + "." + dai.getName();
                        String tempDo = ldInst + "/" + lnPrefix + lnClass + lnInst + "." + doi.getName();
                        // 取冒号之前的部分
                        String refIntAddressPrefix = "";
                        if(StrUtil.isNotEmpty(ref.getIntAddr()) && ref.getIntAddr().contains(":")){
                            refIntAddressPrefix = ref.getIntAddr().substring(0, ref.getIntAddr().indexOf(":"));
                        }
                        ref.setReceivePort(refIntAddressPrefix);
                        // 取冒号之后的部分
                        String refIntAddress = ref.getIntAddr().substring(ref.getIntAddr().indexOf(":") + 1);
                        if (refIntAddress.equals(tempDa) || refIntAddress.equals(tempDo)) {
                            // 完全匹配DA或者DO，都认为是正确的
                            ref.setReceiveDesc(doi.getDesc());
                            ref.setIsAbsoluteMatchReceiveDescFlag(true);
                        } else if (refIntAddress.contains(tempDo)) {
                            ref.setReceiveDesc(doi.getDesc());
                            ref.setIsAbsoluteMatchReceiveDescFlag(false);
                        }
                    }
                }
                break;
            case "Val":
                isInTag = false;
                val.setValue(nodeValue);
                break;
            default:
                break;
        }
        sb.setLength(0);
    }
}
